package gui;

import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import javax.swing.JPanel;

import data.PDMNode;

public class GraphPlane extends JPanel implements MouseListener, MouseMotionListener
{
	private static final long serialVersionUID = 57290300573678880L;

	private List<NodeRep> nodes;
	
	private HashMap<Integer, Integer> nodeAtLevel;
	
	private FontMetrics metrics;
	
	private Color highlightColor;
	
	public GraphPlane()
	{
		nodes = new ArrayList<NodeRep>();
		metrics = this.getFontMetrics(this.getFont());
		this.setBackground(Color.WHITE);
		highlightColor = new Color(200, 0, 0);
		nodeAtLevel = new HashMap<Integer, Integer>();
	}
	
	public void highlightCriticalPath(ArrayList<ArrayList<PDMNode>> paths)
	{
		for(List<PDMNode> path: paths)
		{
			for(PDMNode stone: path)
			{
				for(NodeRep r: nodes)
				{
					if(r.getNode() == stone)
					{
						nodes.get(nodes.indexOf(r)).setDrawColor(highlightColor);
						r.setCritical(true);
					}
				}
			}
		}
	}

	public void updatePositions()
	{
		nodeAtLevel.clear();
		for(NodeRep t: nodes)
		{
			Integer x = t.getNode().getEarlyStart() * 55 + 5;
			t.setX(x);
			Integer res = nodeAtLevel.get(x);
			if(null == res)
			{
				res = 0;
			}

			t.setY(res * 55 + 5);
			
			nodeAtLevel.put(x, res + 1);
		}
	}
	
	public boolean add(PDMNode nr)
	{
		NodeRep r = new NodeRep(nr);
		
		return nodes.add(r);
	}
	
	@Override
	public void paintComponent(Graphics g)
	{
		g.clearRect(0, 0, this.getWidth(), this.getHeight());
		for(NodeRep r: nodes)
		{
			r.draw(g);
			for(PDMNode pdmn: r.getNode().getDependancies())
			{
				NodeRep neighbor = this.getWrapper(pdmn);
				if(neighbor.isCritical && r.isCritical)
					g.setColor(highlightColor);
				else
					g.setColor(Color.BLACK);
				g.drawLine(	r.xCoord, r.yCoord + NodeRep.height / 2,
							neighbor.getX() + NodeRep.width, neighbor.getY() + NodeRep.height / 2);
			}
		}
	}
	
	public NodeRep getWrapper(PDMNode p)
	{
		for(NodeRep n: nodes)
		{
			if(n.getNode() == p)
				return n;
		}
		return null;
	}
	
	private class NodeRep
	{
		private PDMNode node;
		
		private int xCoord;
		private int yCoord;
		private final static int width = 50;
		private final static int height = 50;
		
		private boolean isCritical;
		
		private Color drawColor;
		
		public PDMNode getNode()
		{
			return node;
		}
		
		public NodeRep(PDMNode node)
		{
			this.node = node;
			drawColor = Color.BLACK;
			isCritical = false;
			xCoord = 5;
			yCoord = 5;
		}
		
		public void setCritical(boolean b)
		{
			isCritical = b;
		}
		
		public boolean getCritcal()
		{
			return isCritical;
		}
		
		public void setDrawColor(Color c)
		{
			this.drawColor = c;
		}
		
		public void setX(int x)
		{
			xCoord = x;
		}
		
		public void setY(int y)
		{
			yCoord = y;
		}		
		
		public int getX()
		{
			return xCoord;
		}
		
		public int getY()
		{
			return yCoord;
		}
		
		public void draw(Graphics g)
		{
			g.setColor(drawColor);
			g.drawRoundRect(xCoord, yCoord, width, height, 10, 10);
			g.setColor(Color.BLACK);
				
			String durStr = ((Integer) node.getDuration()).toString();
			String eS = ((Integer) node.getEarlyStart()).toString();
			String lS = ((Integer) node.getLateStart()).toString();
			String eE = ((Integer) node.getEarlyEnd()).toString();
			String lE = ((Integer) node.getLateEnd()).toString();
			String title = node.getNodeKey();
			int Yoffset = metrics.getHeight() / 2;
			int titleOffset = metrics.stringWidth(title) / 2;
			int durStroffset = metrics.stringWidth(durStr) / 2;
			int eEXoffset = metrics.stringWidth(eE);
			int lEXoffset = metrics.stringWidth(lE);
			
			g.drawString(title, xCoord + width / 2 - titleOffset, yCoord + height / 2 + Yoffset - 5);
			g.drawString(durStr, xCoord + width / 2 - durStroffset, yCoord + height - 5);
			g.setColor(Color.BLUE);
			g.drawString(eS, xCoord + 5, yCoord + 5 + Yoffset);
			g.drawString(lS, xCoord + 5, yCoord + height - 5);
			
			g.setColor(Color.MAGENTA);
			g.drawString(eE, xCoord + width - eEXoffset - 5, yCoord + 5 + Yoffset);
			g.drawString(lE, xCoord + width - lEXoffset - 5, yCoord + height - 5);
		}
	}

	@Override
	public void mouseClicked(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void mouseEntered(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void mouseExited(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	private int prevX = Integer.MIN_VALUE;
	private int prevY = Integer.MIN_VALUE;
	private NodeRep selected;
	
	@Override
	public void mousePressed(MouseEvent arg0) {
		selected = nodeAt(arg0.getX(), arg0.getY());
		if(null == selected)
		{
			return;
		}
		prevX = arg0.getX();
		prevY = arg0.getY();
	}

	@Override
	public void mouseReleased(MouseEvent arg0) {
		prevX = Integer.MIN_VALUE;
		prevY = Integer.MIN_VALUE;
	}
	
	private NodeRep nodeAt(int x, int y)
	{
		for(NodeRep nr: nodes)
		{
			if(x >= nr.xCoord &&
			   x <= nr.xCoord + NodeRep.width &&
			   y >= nr.yCoord &&
			   y <= nr.yCoord + NodeRep.height)
			{
				return nr;
			}
		}
		return null;
	}

	@Override
	public void mouseDragged(MouseEvent arg0) {
		if(Integer.MIN_VALUE == prevX || Integer.MIN_VALUE == prevY)
			return;
		int curX = arg0.getX();
		int curY = arg0.getY();
		selected.xCoord = selected.xCoord + (curX - prevX);
		selected.yCoord = selected.yCoord + (curY - prevY);
		prevX = curX;
		prevY = curY;
		repaint();
	}

	@Override
	public void mouseMoved(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}
	
}
